package com.ideaaedi.zoo.diy.artifact.tenant;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.ideaaedi.zoo.commonbase.component.auth.AuthUrlWhitelist;
import com.ideaaedi.zoo.commonbase.component.tenant.DefaultTenantScope;
import com.ideaaedi.zoo.commonbase.component.tenant.TenantScope;
import com.ideaaedi.zoo.commonbase.component.tenant.TenantScopeHolder;
import com.ideaaedi.zoo.commonbase.constant.AutoConfigurationConstant;
import com.ideaaedi.zoo.commonbase.constant.ZooConstant;
import com.ideaaedi.zoo.commonbase.util.ZooContext;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.handler.FullSqlPreparePreHandler;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.interceptor.SqlPreparePreInterceptor;
import com.ideaaedi.zoo.diy.artifact.tenant.holder.MybatisPlusInfoHolder;
import com.ideaaedi.zoo.diy.artifact.tenant.properties.ZooTenantDIYGuideProperties;
import com.ideaaedi.zoo.diy.artifact.tenant.properties.ZooTenantProperties;
import com.ideaaedi.zoo.diy.artifact.tenant.properties.nested.TenantModeEnum;
import com.ideaaedi.zoo.diy.artifact.tenant.tenant.LikeTenantLineInnerInterceptor;
import com.ideaaedi.zoo.diy.artifact.tenant.tenant.TenantScopeAspect;
import com.ideaaedi.zoo.diy.artifact.tenant.tenant.VisitorTenantFilter;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 多租户 自动配置
 */
@Slf4j
@Import(TenantScopeAspect.class)
@AutoConfigureOrder(AutoConfigurationConstant.ZOO_TENANT_AUTO_CONFIGURATION_ORDER)
@EnableConfigurationProperties({ZooTenantDIYGuideProperties.class, ZooTenantProperties.class})
@ConditionalOnProperty(value = ZooTenantAutoConfiguration.ENABLE, havingValue = "true", matchIfMissing = true)
public class ZooTenantAutoConfiguration {
    
    public static final String ENABLE = "zoo.switch.artifact-tenant.enable";
    
    @Bean
    public MybatisPlusInfoHolder mybatisPlusInfoHolder(ApplicationContext applicationContext) {
        return new MybatisPlusInfoHolder(applicationContext);
    }
    
    /**
     * 插件配置
     */
    @Bean(name = AutoConfigurationConstant.MYBATIS_PLUS_INTERCEPTOR_BEAN_NAME)
    public MybatisPlusInterceptor mybatisPlusInterceptor(MybatisPlusInfoHolder mybatisPlusInfoHolder,
                                                         ZooTenantProperties zooTenantProperties,
                                                         List<FullSqlPreparePreHandler> fullSqlPreparePreHandlers) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        if (!CollectionUtils.isEmpty(fullSqlPreparePreHandlers)) {
            interceptor.addInnerInterceptor(new SqlPreparePreInterceptor(fullSqlPreparePreHandlers));
        }
        TenantModeEnum tenantMode = zooTenantProperties.getMode();
        switch (tenantMode) {
            case LIKE_RIGHT:
                // 租户插件
                interceptor.addInnerInterceptor(new LikeTenantLineInnerInterceptor(new TenantLineHandler() {
                    @Override
                    public Expression getTenantId() {
                        // 先从会话级临时数据域中查
                        TenantScope tenantScope = TenantScopeHolder.SESSION_DATA_SCOPE_HOLDER.get().peekFirst();
                        // 在从用户登录态中查
                        if (tenantScope == null) {
                            tenantScope = ZooContext.TenantCt.currTenantScope();
                        }
                        Objects.requireNonNull(tenantScope, "getTenantId fail. tenantScope cannot be null. curr context -> "
                                + JSON.toJSONString(ZooContext.unmodifiableContext()));
                        return new StringValue(
                                JSON.toJSONString(
                                        DefaultTenantScope.of(
                                                tenantScope.insertTenant(),
                                                tenantScope.readableTenants(),
                                                tenantScope.modifiableTenants()
                                        )
                                )
                        );
                    }
                    
                    @Override
                    public String getTenantIdColumn() {
                        return ZooConstant.TENANT_COLUMN_NAME;
                    }
                    
                    @Override
                    public boolean ignoreTable(String tableName) {
                        Set<String> tenantTables = mybatisPlusInfoHolder.getTenantTables();
                        if (CollectionUtils.isEmpty(tenantTables)) {
                            return true;
                        }
                        if (tableName.contains(ZooConstant.MYSQL_ANTI_ESCAPE)) {
                            tableName = tableName.replace(ZooConstant.MYSQL_ANTI_ESCAPE, ZooConstant.EMPTY);
                        }
                        return !tenantTables.contains(tableName);
                    }
                }, zooTenantProperties));
                break;
            case IN_EQUALS:
                throw new UnsupportedOperationException("Not supported at this time tenantMode '" + tenantMode + "'.");
                ///break;
            default:
                throw new UnsupportedOperationException("non-support tenantMode '" + tenantMode + "'.");
        }
        
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        log.info("enable mybatis-plus interceptors -> {}.",
                interceptor.getInterceptors().stream().map(x -> x.getClass().getSimpleName()).collect(Collectors.toList()));
        return interceptor;
    }
    
    @Bean
    @ConditionalOnMissingBean(name = "visitorTenantFilter")
    public FilterRegistrationBean<VisitorTenantFilter> visitorTenantFilter(ZooTenantProperties zooTenantProperties,
                                                                           @Autowired(required = false) AuthUrlWhitelist authUrlWhitelist) {
        FilterRegistrationBean<VisitorTenantFilter> frBean = new FilterRegistrationBean<>();
        frBean.setFilter(new VisitorTenantFilter(zooTenantProperties.getVisitor(), authUrlWhitelist));
        frBean.setOrder(AutoConfigurationConstant.ZOO_VISITOR_TENANT_FILTER_ORDER);
        frBean.addUrlPatterns("/*");
        return frBean;
    }
}
